import { Vec2 } from "../d2/math/Vec2";
import { EventMgr } from "../event/EventMgr";
import { Bits } from "../util/Bits";
import { Cls } from "../util/Defined";
import { IRecyclePoolObj, RecyclePool } from "../util/Pool";
import { ECSCmp } from "./ECSCmp";
import { ECSScene } from "./ECSScene";

/**
 * [ECS]实体
 */
export class ECSEntity extends EventMgr<ECSEventData> implements IRecyclePoolObj {

	pool: RecyclePool<this>;
	poolIdx: number;
	/** 实体id */
	id: number;
	/** 所在场景 */
	scene: ECSScene;
	/** 是否已应用初始组件 */
	applyed: boolean;
	/** 是否已执行onStart */
	started: boolean;
	/** 被标记移除 在帧循环结束前将被移除 */
	removed: boolean;

	/** 在场景中的坐标 */
	readonly pos = new Vec2();
	/** 拥有的组件类型 bits */
	readonly cmpBits = new Bits();
	/** 所有组件映射  */
	readonly cmpMap = new Map<Cls<ECSCmp>, ECSCmp>();

	onAlloc() {
		this.removed = false;
		this.pos.resetPos();
	}

	onFree(): void {
		this.cmpBits.reset();
		this.offAll();
		this.applyed = false;
		this.started = false;
	}

	/**
	 * 应用初始组件
	 * (需要手动调用)
	 */
	apply() {
		if (this.applyed) {
			return;
		}
		this.applyed = true;
		this.cmpMap.forEach(this.applyCmp, this);
		return this;
	}

	/**
	 * 执行实体onStart
	 * (由实体管理器调用 请不要手动调用)
	 */
	start() {
		if (this.started) {
			return;
		}
		this.started = true;
		this.cmpMap.forEach(this.startCmp, this);
	}

	private applyCmp(cmp: ECSCmp) {
		cmp.onApply?.();
	}

	private startCmp(cmp: ECSCmp) {
		cmp.onStart?.();
	}

	/**
	 * 从场景移除时(场景销毁时不会调用)
	 */
	onRemove() {
		this.cmpMap.forEach(this.scene.cmpMgr.remove, this.scene.cmpMgr);
	}

	/**
	 * 从场景中移除自己
	 */
	removeSelf() {
		this.scene.removeEntity(this);
	}

	// ============ 组件 ============

	/**
	 * 是否存在对应组件
	 * @param cmpType 组件类型
	 */
	hasCmp(cmpType: Cls<ECSCmp>) {
		return this.cmpMap.has(cmpType);
	}

	/**
	 * 获取对应组件类型的组件
	 * @param cmpType 组件类型
	 */
	getCmp<T extends ECSCmp>(cmpType: Cls<T>): T {
		return this.cmpMap.get(cmpType) as T;
	}

	/**
	 * 通过idx标识获取组件
	 * @param idx 
	 */
	getCmpByIdx(idx: number) {
		return this.cmpMap.get(this.scene.cmpMgr.getType(idx));
	}

	/**
	 * 获取对应组件类型的组件 如果没有则创建
	 * @param cmpType 组件类型
	 */
	getOrCreateCmp<T extends ECSCmp>(cmpType: Cls<T>): T {
		return this.getCmp(cmpType) || this.createCmp(cmpType);
	}

	/**
	 * 创建组件
	 * @param cmpType 组件类型
	 */
	createCmp<T extends ECSCmp>(cmpType: Cls<T>): T {
		return this.scene.cmpMgr.create(this, cmpType);
	}

	/**
	 * 移除组件
	 * @param cmpType 组件类型
	 */
	removeCmp(cmpType: Cls<ECSCmp>) {
		this.scene.cmpMgr.remove(this.cmpMap.get(cmpType));
	}
}
